﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Reflection.Controller
{
    class OpCodesFactory
    {
        /// <summary>
        /// 压入整数堆栈方法 包括枚举
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static void LdIntOrEnum(ILGenerator il, int value)
        {
            if (value == -1)
                il.Emit(OpCodes.Ldc_I4_M1);
            else if (value == 0)
                il.Emit(OpCodes.Ldc_I4_0);
            else if (value == 1)
                il.Emit(OpCodes.Ldc_I4_1);
            else if (value == 2)
                il.Emit(OpCodes.Ldc_I4_2);
            else if (value == 3)
                il.Emit(OpCodes.Ldc_I4_3);
            else if (value == 4)
                il.Emit(OpCodes.Ldc_I4_4);
            else if (value == 5)
                il.Emit(OpCodes.Ldc_I4_5);
            else if (value == 6)
                il.Emit(OpCodes.Ldc_I4_6);
            else if (value == 7)
                il.Emit(OpCodes.Ldc_I4_7);
            else if (value == 8)
                il.Emit(OpCodes.Ldc_I4_8);
            else if (value > 8)
                il.Emit(OpCodes.Ldc_I4, value);
            else if (value < -1)
                il.Emit(OpCodes.Ldc_I4, value);
            else
                throw new Exception("unknown index to ldc_i4");
        }

        public static void LdChar(ILGenerator il, char value)
        {
            if (value == 0)
                il.Emit(OpCodes.Ldc_I4_0);
            else if (value == 1)
                il.Emit(OpCodes.Ldc_I4_1);
            else if (value == 2)
                il.Emit(OpCodes.Ldc_I4_2);
            else if (value == 3)
                il.Emit(OpCodes.Ldc_I4_3);
            else if (value == 4)
                il.Emit(OpCodes.Ldc_I4_4);
            else if (value == 5)
                il.Emit(OpCodes.Ldc_I4_5);
            else if (value == 6)
                il.Emit(OpCodes.Ldc_I4_6);
            else if (value == 7)
                il.Emit(OpCodes.Ldc_I4_7);
            else if (value == 8)
                il.Emit(OpCodes.Ldc_I4_8);
            else if (value > 8)
                il.Emit(OpCodes.Ldc_I4, value);
            else
                throw new Exception("unknown index to ldc_i4");
        }

        public static void LdBoolean(ILGenerator il, bool value)
        {
            if (value)
                il.Emit(OpCodes.Ldc_I4_1);
            else
                il.Emit(OpCodes.Ldc_I4_0);
        }

        public static void LdLong(ILGenerator il, long value)
        {
            il.Emit(OpCodes.Ldc_I8, value);
        }

        public static void LdFloat(ILGenerator il, float value)
        {
            il.Emit(OpCodes.Ldc_R4, value);
        }

        public static void LdDouble(ILGenerator il, double value)
        {
            il.Emit(OpCodes.Ldc_R8, value);
        }

        public static void LdString(ILGenerator il, string value)
        {
            if (string.IsNullOrEmpty(value))
                il.Emit(OpCodes.Ldnull);
            else
                il.Emit(OpCodes.Ldstr, value);
        }

        public static void LdNull(ILGenerator il)
        {
            il.Emit(OpCodes.Ldnull);
        }

        /// <summary>
        /// 根据传入的数据类型，获取数组元素的op
        /// arrayType = 数组的类型 arrayIndex=数组索引
        /// </summary>
        /// <param name="reflectType"></param>
        /// <returns></returns>
        public static void LdArrayValue(ILGenerator il, Type arrayType, int arrayIndex)
        {
            LdIntOrEnum(il, arrayIndex);

            if (!arrayType.IsValueType)
                il.Emit(OpCodes.Ldelem_Ref);

            else if (typeof(byte).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_I1);

            else if (typeof(short).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_I2);

            else if (typeof(Int32).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_I4);

            else if (typeof(Int64).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_I8);

            else if (typeof(float).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_R4);

            else if (typeof(double).Equals(arrayType))
                il.Emit(OpCodes.Ldelem_R8);

            else
                il.Emit(OpCodes.Ldelem_Ref);
        }

        /// <summary>
        /// 压入一个typeof 并放入堆栈
        /// </summary>
        /// <param name="type"></param>
        public static void LdType(ILGenerator il, Type type)
        {
            il.Emit(OpCodes.Ldtoken, type);
            il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"));
        }

        /// <summary>
        /// 加载输入的参数
        /// </summary>
        /// <param name="generator"></param>
        /// <param name="index"></param>
        public static void LdArgument(ILGenerator il, int index)
        {
            if (index == 0)
                il.Emit(OpCodes.Ldarg_0);

            else if (index == 1)
                il.Emit(OpCodes.Ldarg_1);

            else if (index == 2)
                il.Emit(OpCodes.Ldarg_2);

            else if (index == 3)
                il.Emit(OpCodes.Ldarg_3);

            else
                il.Emit(OpCodes.Ldarg_S, index);
        }

        public static void LdLocalVariable(ILGenerator il, int index)
        {
            if (index == 0)
                il.Emit(OpCodes.Ldloc_0);

            else if (index == 1)
                il.Emit(OpCodes.Ldloc_1);

            else if (index == 2)
                il.Emit(OpCodes.Ldloc_2);

            else if (index == 3)
                il.Emit(OpCodes.Ldloc_3);

            else
                il.Emit(OpCodes.Ldloc_S, index);
        }

        public static void SaveLocalVariable(ILGenerator il, int index)
        {
            if (index == 0)
                il.Emit(OpCodes.Stloc_0);

            else if (index == 1)
                il.Emit(OpCodes.Stloc_1);

            else if (index == 2)
                il.Emit(OpCodes.Stloc_2);

            else if (index == 3)
                il.Emit(OpCodes.Stloc_3);

            else
                il.Emit(OpCodes.Stloc_S, index);
        }

        /// <summary>
        /// 判断是否需要装箱
        /// </summary>
        /// <param name="generator"></param>
        /// <param name="type"></param>
        public static void BoxIfNeeded(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Box, type);
            }
        }

        /// <summary>
        /// 判断是否需要拆箱
        /// </summary>
        /// <param name="generator"></param>
        /// <param name="type"></param>
        public static void UnboxIfNeeded(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, type);
            }
        }

        public static void CastIfNeeded(ILGenerator il, Type type)
        {
            if (!type.IsValueType)
            {
                il.Emit(OpCodes.Castclass, type);
            }
        }

        /// <summary>
        /// 创建对象 并加载在堆栈
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="argumentTypes"></param>
        public static void CreateObject<T>(ILGenerator il, params Type[] argumentTypes)
        {
            il.Emit(OpCodes.Newobj, typeof(T).GetConstructor(argumentTypes));
        }

        public static void CreateObject(ILGenerator il, TypeBuilder builder)
        {
            il.Emit(OpCodes.Newobj, builder.GetConstructor(Type.EmptyTypes));
        }

        public static void CreateObject(ILGenerator il, Type objectType, params Type[] argumentTypes)
        {
            il.Emit(OpCodes.Newobj, objectType.GetConstructor(argumentTypes));
        }

        public static void CreateObject(ILGenerator il, ConstructorInfo info)
        {
            il.Emit(OpCodes.Newobj, info);
        }

        public static void CreateArray(ILGenerator il, Type elementType, int length)
        {
            LdIntOrEnum(il, length);
            il.Emit(OpCodes.Newarr, elementType);
        }

        public static void ReturnMethod(ILGenerator il)
        {
            il.Emit(OpCodes.Ret);
        }

        public static void Donothing(ILGenerator il)
        {
            il.Emit(OpCodes.Nop);
        }

        /// <summary>
        /// 返回值是接口等多态的方法
        /// </summary>
        /// <typeparam name="Class"></typeparam>
        /// <typeparam name="Attribute"></typeparam>
        /// <param name="generator"></param>
        public static void CallVirtMethod<Class, Attribute>(ILGenerator il)
        {
            //http://www.cnblogs.com/joewang/articles/1537612.html call vs callvirt = 多态

            il.Emit(OpCodes.Callvirt, ReflectionFactory.GetMethodInfo<Class, Attribute>());
        }

        public static void CallVirtMethod(ILGenerator il, MethodInfo info)
        {
            if (info == null)
                throw Exceptions.UnexpectedResultException("missing methodinfo when call virt method.");

            //http://www.cnblogs.com/joewang/articles/1537612.html call vs callvirt = 多态

            il.Emit(OpCodes.Callvirt, info);
        }

        public static void CallMethod<Class, Attribute>(ILGenerator il)
        {
            //http://www.cnblogs.com/joewang/articles/1537612.html call vs callvirt = 多态

            il.Emit(OpCodes.Call, ReflectionFactory.GetMethodInfo<Class, Attribute>());
        }

        public static void CallMethod(ILGenerator il, MethodInfo info)
        {
            if (info == null)
                throw Exceptions.UnexpectedResultException("missing methodinfo when call method.");

            //http://www.cnblogs.com/joewang/articles/1537612.html call vs callvirt = 多态

            il.Emit(OpCodes.Call, info);
        }

        public static void CallSetvirtProperty<Class, Attribute>(ILGenerator il)
        {
            il.Emit(OpCodes.Callvirt, ReflectionFactory.GetPropertyInfo<Class, Attribute>().GetSetMethod(true));
        }

        public static void CallGetvirtProperty<Class, Attribute>(ILGenerator il)
        {
            il.Emit(OpCodes.Callvirt, ReflectionFactory.GetPropertyInfo<Class, Attribute>().GetGetMethod(true));
        }

        /// <summary>
        /// 要求之前在堆栈上放入了一个bool值
        /// </summary>
        /// <param name="generator"></param>
        /// <returns></returns>
        internal static System.Reflection.Emit.Label IfTrue(ILGenerator il)
        {
            //http://forums.silverlight.net/forums/p/183945/418444.aspx

            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Ceq);
            System.Reflection.Emit.Label label = il.DefineLabel();
            il.Emit(OpCodes.Brtrue_S, label);
            return label;
        }

        internal static void EndIf(ILGenerator il, Label ifLabel)
        {
            il.MarkLabel(ifLabel);
        }

        internal static void LdField(ILGenerator generator, FieldBuilder fieldBuilder)
        {
            generator.Emit(OpCodes.Ldfld, fieldBuilder);
        }

        internal static void StField(ILGenerator generator, FieldBuilder fieldBuilder)
        {
            generator.Emit(OpCodes.Stfld, fieldBuilder);
        }
    }
}
